﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace NearAop
{
    public static class AopHelper
    {
        private const string ENTER = "\r\n";


        /// <summary>
        /// 转换类型
        /// </summary>
        /// <typeparam name="T">转换类型</typeparam>
        /// <param name="callPath">调用路径</param>
        /// <param name="testObject">测试对象</param>
        public static T CastType<T>(string callPath, object testObject) 
        {
            NullThrow(callPath, testObject);
            if (testObject is T result) return result;
            var type = testObject.GetType();
            var interfaces = type.GetInterfaces().Select(x=> x.FullName);
            var errMsg = new StackTrace(true)
                .GetCallStep($"[{callPath}] not is [{typeof(T).FullName}],请参考文档SetProxySourceAttribute特性");
            throw new InvalidCastException(errMsg);
        }

        /// <summary>
        /// 遇到空值抛出异常
        /// </summary>
        private static void NullThrow(string callPath, object testObject)
        {
            if (null == testObject) throw GetNullThrow(callPath);
        }


        /// <summary>
        /// 测试是否相等
        /// </summary>
        public static bool TestEquals<T>(T leftValue, T rightValue)
        {
            return leftValue?.Equals(rightValue) ?? rightValue == null;
        }

        /// <summary>
        /// 空测试(强制转换)
        /// </summary>
        public static T TestNull<T>(string callPath, object testObject)
        {
            NullThrow(callPath, testObject);
            return (T)testObject;
        }

        public static void TestRun(Func<bool> test, Action run)
        {
            if (test()) run();
        }

        private static Exception GetNullThrow(string callPath)
        {
            var errMsg = new StackTrace(true)
                .GetCallStep($"{callPath} is empty,未将对象引用设置到对象的实例.");
            throw new NullReferenceException(errMsg);
        }

        /// <summary>
        /// 为代理类提供目标类的实例
        /// </summary>
        /// <param name="proxy">代理类</param>
        /// <param name="target">目标类实例</param>
        /// <param name="proxyPath">代理类路径</param>
        public static void SetProxySourceData(object proxy, object target, string proxyPath)
        {
            CastType<IAopHandlerProxy>(proxyPath, proxy).SourceData = target;
        }

        /// <summary>
        /// 返回成员名称
        /// </summary>
        public static string GetMemberName(this Lazy<AopParamInfo> paramProvider)
        {
            return paramProvider.Value.MemberInfo.Name;
        }

        /// <summary>
        /// 返回成员
        /// </summary>
        public static T GetMember<T>(this Lazy<AopParamInfo> paramProvider) where T : MemberInfo
        {
            return paramProvider.Value.MemberInfo as T;
        }

        /// <summary>
        /// 调用属性变更通知(调用类中的IAopHandlerPropertyChanging.RaiseEvent方法)
        /// </summary>
        /// <param name="sender">当前对象(me 或 this)</param>
        /// <param name="propertyName">属性名称</param>
        public static void RaiseEventPropertyChanged(object sender, string propertyName)
        {
            if (!(sender is IAopHandlerNotifyPropertyChanged propertyChanging))
            {
                var err = $"'{sender.GetType()}'没有实现接口'{typeof(IAopHandlerNotifyPropertyChanged)}',可能出现的原因是:类显式实现了INotifyPropertyChanged接口";
                throw new NotImplementedException(err);
            }
            propertyChanging.RaiseEvent(sender, propertyName);
        }

        private static string GetCallStep(this StackTrace stackTrace, string fieldName)
        {
            var ignore = typeof(AopHelper).ToString();
            var items = stackTrace.GetFrames()
                .Select(x => x.GetMethod())
                .Select(getCallStep)
                .Where(x => !x.StartsWith(ignore))
                .Select((x, i) => $"{i,-3}{x}");
            return $"{fieldName}{ENTER}{string.Join(ENTER, items)}";
            string getCallStep(MethodBase method) => $"{method.DeclaringType} : {method}";
        }
    }
}
